﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace MirrorFolder
{
    class Gl
    {
        public static string DPathLogs;
    }

    public enum ScheduleMode { None, EveryDay, EveryHour, EveryPeriod }
    public enum ElementMode { Mirror, Archive }
    public enum ArchiveMode { Diff, Inc }

    /// <summary>Расписание</summary>
    public class Schedule
    {
        public ScheduleMode Mode = ScheduleMode.None;

        /// <summary>Дни недели</summary>
        public bool[] DaysOfWeek = new bool[7];
        /// <summary>Время выполнения в секундах</summary>
        public int TimeOfDay = 0;

        /// <summary>Часы</summary>
        public bool[] Hours = new bool[24];
        /// <summary>Минута часа</summary>
        public byte Minute = 0;

        /// <summary>Дата начала</summary>
        public DateTime StartDate = DateTime.Now;
        /// <summary>Период выполнения в секундах</summary>
        public int Period = 60;

        public int PeriodDays { get { return (int)(DateTime.MinValue.AddSeconds(Period) - DateTime.MinValue).TotalDays; } }
        public int PeriodHours { get { return (DateTime.MinValue.AddSeconds(Period) - DateTime.MinValue).Hours; } }
        public int PeriodMinuts { get { return (DateTime.MinValue.AddSeconds(Period) - DateTime.MinValue).Minutes; } }
        public int PeriodSeconds { get { return (DateTime.MinValue.AddSeconds(Period) - DateTime.MinValue).Seconds; } }


        public DateTime NextSync = DateTime.MinValue;

        public Schedule()
        {
            ;
        }

        public Schedule(BinaryReader Br)
        {
            Load(Br);
        }

        public void SetNextSyncDate()
        {
            if (Mode == ScheduleMode.None) throw new Exception("Попытка установить следующую дату по расписанию с неустановленными параметрами расписания");

            DateTime actDt = DateTime.Now;

            switch (Mode)
            {
                case ScheduleMode.EveryPeriod:
                    {
                        NextSync = StartDate.AddSeconds(Math.Round((actDt - StartDate).TotalSeconds / Period) * Period);
                        if (NextSync < actDt) NextSync = NextSync.AddSeconds(Period);
                        return;
                    }
                case ScheduleMode.EveryDay:
                    {
                        NextSync = actDt.Date.AddSeconds(TimeOfDay);
                        if (NextSync < actDt) NextSync = NextSync.AddDays(1);
                        int cnt = 0;
                        while (true)
                        {
                            int actDayOfWeek = (int)NextSync.DayOfWeek - 1;
                            if (actDayOfWeek == -1) actDayOfWeek = 6;
                            if (DaysOfWeek[actDayOfWeek]) break;
                            NextSync = NextSync.AddDays(1);
                            if (cnt > 7) throw new Exception("Ошибка получения следующей даты. Ни один день не выбран");
                            cnt++;
                        }
                        return;
                    }
                case ScheduleMode.EveryHour:
                    {
                        NextSync = actDt.Date.AddHours(actDt.Hour).AddMinutes(Minute);
                        if (NextSync < actDt) NextSync = NextSync.AddHours(1);
                        int cnt = 0;
                        while (true)
                        {
                            if (Hours[NextSync.Hour]) break;
                            NextSync = NextSync.AddHours(1);
                            if (cnt > 24) throw new Exception("Ошибка получения следующей даты. Ни один час не выбран");
                            cnt++;
                        }
                        return;
                    }
                default: throw new Exception("Попытка запосить дату следующей синхронизации пр неустановленных параметрах");
            }
        }

        #region *** Load/Save *******************************************

        public void Load(BinaryReader Br)
        {
            int ver = Br.ReadByte();
            switch (ver)
            {
                case 0: Load0(Br); break;
                default: throw new Exception("Неподдерживаемая версия расписания для элемента");
            }
        }

        void Load0(BinaryReader Br)
        {
            Mode = (ScheduleMode)Br.ReadByte();
            for (int i = 0; i < 7; i++) DaysOfWeek[i] = Br.ReadBoolean();
            TimeOfDay = Br.ReadInt32();
            for (int i = 0; i < 24; i++) Hours[i] = Br.ReadBoolean();
            Minute = Br.ReadByte();
            StartDate = new DateTime(Br.ReadInt64());
            Period = Br.ReadInt32();
            NextSync = new DateTime(Br.ReadInt64());
        }

        public void Save(BinaryWriter Bw)
        {
            Bw.Write((byte)0);
            Bw.Write((byte)Mode);
            for (int i = 0; i < 7; i++) Bw.Write(DaysOfWeek[i]);
            Bw.Write(TimeOfDay);
            for (int i = 0; i < 24; i++) Bw.Write(Hours[i]);
            Bw.Write(Minute);
            long l = StartDate.Ticks;
            Bw.Write(l);
            Bw.Write(Period);
            Bw.Write(NextSync.Ticks);
        }

        #endregion *** Load/Save *******************************************

        public Schedule Clone()
        {
            Schedule ret = new Schedule();
            ret.Mode = Mode;
            ret.DaysOfWeek = (bool[])DaysOfWeek.Clone();
            ret.TimeOfDay = TimeOfDay;
            ret.Hours = (bool[])Hours.Clone();
            ret.Minute = Minute;
            ret.StartDate = StartDate;
            ret.Period = Period;
            ret.NextSync = NextSync;
            return ret;
        }

        public override string ToString()
        {
            if (Mode == ScheduleMode.None) return "нет";

            if (Mode == ScheduleMode.EveryPeriod)
                return string.Format("каждые {0}д {1}ч {2}м {3}с начиная с {4}", PeriodDays, PeriodHours, PeriodMinuts, PeriodSeconds, StartDate.ToString("dd.MM.yyyy HH:mm"));

            if (Mode == ScheduleMode.EveryHour)
                return string.Format("каждый час в {0}мин", Minute);

            if (Mode == ScheduleMode.EveryDay)
            {
                TimeSpan ts = DateTime.MinValue.AddSeconds(TimeOfDay) - DateTime.MinValue;
                return string.Format("каждый день в {0}:{1}", ((int)ts.TotalHours).ToString("00"), ts.Minutes.ToString("00"));
            }


            return "";
        }
    }

    /// <summary>Настройки элемента</summary>
    public class MElementSettings
    {
        public ElementMode Mode = ElementMode.Mirror;
        /// <summary>Исходный каталог</summary>
        public string SourceDir = "";
        /// <summary>Каталог зеркала или архива</summary>
        public string DestDir = "";
        /// <summary>Двусторонняя синхронизация</summary>
        public bool DualSync = false;
        /// <summary>Расписание</summary>
        public Schedule Schd = new Schedule();
        /// <summary>Размер линии</summary>
        public byte LineSize = 0;
        /// <summary>Размер одной линии архива</summary>
        public byte LinesCount = 0;
        /// <summary>Количество линий архива</summary>
        public ArchiveMode ArchMode = ArchiveMode.Diff;

        public MElementSettings()
        {
            ;
        }

        public MElementSettings(BinaryReader Br)
        {
            Load(Br);
        }

        #region *** Load/Save *******************************************

        public void Load(BinaryReader Br)
        {
            int ver = Br.ReadByte();
            switch (ver)
            {
                case 0: Load0(Br); break;
                default: throw new Exception("Неподдерживаемая версия настроек для элемента");
            }
        }

        void Load0(BinaryReader Br)
        {
            Mode = (ElementMode)Br.ReadByte();
            SourceDir = Br.ReadString();
            DestDir = Br.ReadString();
            DualSync = Br.ReadBoolean();
            Schd = new Schedule(Br);
            LineSize = Br.ReadByte();
            LinesCount = Br.ReadByte();
            ArchMode = (ArchiveMode)Br.ReadByte();
        }

        public void Save(BinaryWriter Bw)
        {
            Bw.Write((byte)0);
            Bw.Write((byte)Mode);
            Bw.Write(SourceDir);
            Bw.Write(DestDir);
            Bw.Write(DualSync);
            Schd.Save(Bw);
            Bw.Write(LineSize);
            Bw.Write(LinesCount);
            Bw.Write((byte)ArchMode);
        }

        #endregion *** Load/Save *******************************************

        public MElementSettings Clone()
        {
            MElementSettings ret = new MElementSettings();
            ret.Mode = Mode;
            ret.SourceDir = SourceDir;
            ret.DestDir = DestDir;
            ret.DualSync = DualSync;
            ret.Schd = Schd.Clone();
            ret.ArchMode = ArchMode;
            ret.LineSize = LineSize;
            ret.LinesCount = LinesCount;
            return ret;
        }

    }

}
